﻿#include "Material/Material.h"

#include "Object/NewObject.h"
#include "Material.h"
#include "Path/Paths.h"

#include "Render/RenderContext.h"
#include "MaterialSubsystem.h"
#include "Output/Console.h"

RMaterial::RMaterial()
{
    RenderMaterial=std::make_shared<CRenderMaterial>();
}

void RMaterial::SyncParameterToRenderThread()
{
    TVector<std::shared_ptr<CRenderTexture2D>> RenderTexture2Ds;
    for(auto& Texture: this->Texture2Ds)
    {
        RenderTexture2Ds.Add(Texture->GetRenderTexture());
    }
    TVector<std::shared_ptr<CRenderTextureCube>> RenderTextureCubes;
    for(auto& Texture: this->TextureCubes)
    {
        RenderTextureCubes.Add(Texture->GetRenderTexture());
    }

    RenderMaterial->SetParameters(FloatParameters,RenderTexture2Ds,RenderTextureCubes);
}

void RMaterial::SyncToRenderThread()
{
    std::shared_ptr<CRenderShader> RenderVertexShader;
    std::shared_ptr<CRenderShader> RenderFragmentShader;
    ERenderMaterialType RenderType;

    if(!VertexShader || !FragmentShader)
    {
        //缺少着色器，使用默认着色器
        RenderVertexShader=RMaterialSubsystem::Get()->DefaultVertexShader->GetRenderShader();
        RenderFragmentShader=RMaterialSubsystem::Get()->DefaultFragmentShader->GetRenderShader();
        RenderType=ERenderMaterialType::Opaque;
    }
    else
    {
        RenderVertexShader=VertexShader->GetRenderShader();
        RenderFragmentShader=FragmentShader->GetRenderShader();
        RenderType=Type;

    }

    TVector<std::shared_ptr<CRenderTexture2D>> RenderTexture2Ds;
    for(auto& Texture: this->Texture2Ds)
    {
        RenderTexture2Ds.Add(Texture->GetRenderTexture());
    }
    TVector<std::shared_ptr<CRenderTextureCube>> RenderTextureCubes;
    for(auto& Texture: this->TextureCubes)
    {
        RenderTextureCubes.Add(Texture->GetRenderTexture());
    }

    RenderMaterial->SetAllMaterialThings(
        RenderVertexShader
        ,RenderFragmentShader
        ,bDoubleSide
        ,RenderType
        ,FloatParameters
        ,RenderTexture2Ds
        ,RenderTextureCubes
    );
}

void RMaterial::PostLoadFromAsset()
{
    SyncToRenderThread();
}

std::shared_ptr<CRenderMaterial> RMaterial::GetRenderMaterial()
{
    return RenderMaterial;
}

void RMaterial::CopyFrom(TSharedObjectPtr<RMaterial> InMaterial)
{
    FragmentShader=InMaterial->FragmentShader;
    VertexShader=InMaterial->VertexShader;
    Type=InMaterial->Type;
    FloatParameters=InMaterial->FloatParameters;
    Texture2Ds=InMaterial->Texture2Ds;
    ParameterDescriptions=InMaterial->ParameterDescriptions;


    SyncToRenderThread();
}

void RMaterial::SetFloatParameter(CString ParameterName, float Value)
{
    if(!ParameterDescriptions.Contains(ParameterName))
    {
        WH::PrintLine(U"材质中没有参数\"{}\"",ParameterName);
        return;
    }

    auto& ParameterDescription=ParameterDescriptions[ParameterName];
    if(ParameterDescription.Type!=EMaterialParameterType::Float)
    {
        WH::PrintLine(U"参数\"{}\"不是浮点参数",ParameterName);
        return;
    }

    FloatParameters[ParameterDescription.Offset]=Value;

    SyncParameterToRenderThread();
}

void RMaterial::SetVector2Parameter(CString ParameterName, const SVec2 &Value)
{
    if(!ParameterDescriptions.Contains(ParameterName))
    {
        WH::PrintLine(U"材质中没有参数\"{}\"",ParameterName);
        return;
    }

    auto& ParameterDescription=ParameterDescriptions[ParameterName];
    if(ParameterDescription.Type!=EMaterialParameterType::Vector2)
    {
        WH::PrintLine(U"参数\"{}\"不是二维向量参数",ParameterName);
        return;
    }

    FloatParameters[ParameterDescription.Offset]=Value.X;
    FloatParameters[ParameterDescription.Offset+1]=Value.Y;

    SyncParameterToRenderThread();
}

void RMaterial::SetVector3Parameter(CString ParameterName, const SVec3 &Value)
{
    if(!ParameterDescriptions.Contains(ParameterName))
    {
        WH::PrintLine(U"材质中没有参数\"{}\"",ParameterName);
        return;
    }

    auto& ParameterDescription=ParameterDescriptions[ParameterName];
    if(ParameterDescription.Type!=EMaterialParameterType::Vector3)
    {
        WH::PrintLine(U"参数\"{}\"不是三维向量参数",ParameterName);
        return;
    }

    FloatParameters[ParameterDescription.Offset]=Value.X;
    FloatParameters[ParameterDescription.Offset+1]=Value.Y;
    FloatParameters[ParameterDescription.Offset+2]=Value.Z; 

    SyncParameterToRenderThread();
}

void RMaterial::SetVector4Parameter(CString ParameterName, const SVec4 &Value)
{
    if(!ParameterDescriptions.Contains(ParameterName))
    {
        WH::PrintLine(U"材质中没有参数\"{}\"",ParameterName);
        return;
    }

    auto& ParameterDescription=ParameterDescriptions[ParameterName];
    if(ParameterDescription.Type!=EMaterialParameterType::Vector4)
    {
        WH::PrintLine(U"参数\"{}\"不是四维向量参数",ParameterName);
        return;
    }

    FloatParameters[ParameterDescription.Offset]=Value.X;
    FloatParameters[ParameterDescription.Offset+1]=Value.Y;
    FloatParameters[ParameterDescription.Offset+2]=Value.Z;
    FloatParameters[ParameterDescription.Offset+3]=Value.W;

    SyncParameterToRenderThread();
}

void RMaterial::SetTexture2DParameter(CString ParameterName, TSharedObjectPtr<RTexture2D> Texture)
{
    if(!ParameterDescriptions.Contains(ParameterName))
    {
        WH::PrintLine(U"材质中没有参数\"{}\"",ParameterName);
        return;
    }

    auto& ParameterDescription=ParameterDescriptions[ParameterName];
    if(ParameterDescription.Type!=EMaterialParameterType::Texture2D)
    {
        WH::PrintLine(U"参数\"{}\"不是二维纹理参数",ParameterName);
        return;
    }

    Texture2Ds[ParameterDescription.Offset]=Texture;
    SyncParameterToRenderThread();
}

void RMaterial::SetTexture2DByIndex(int64_t Index, TSharedObjectPtr<RTexture2D> Texture)
{
    if(Index>=Texture2Ds.Num())
    {
        WH::PrintLine(U"材质中没有索引为{}的二维纹理参数",Index);
        return;
    }

    Texture2Ds[Index]=Texture;
    SyncParameterToRenderThread();
}

void RMaterial::SetTextureCubeParameter(CString ParameterName, TSharedObjectPtr<RTextureCube> Texture)
{
    if(!ParameterDescriptions.Contains(ParameterName))
    {
        WH::PrintLine(U"材质中没有参数\"{}\"",ParameterName);
        return;
    }

    auto& ParameterDescription=ParameterDescriptions[ParameterName];
    if(ParameterDescription.Type!=EMaterialParameterType::TextureCube)
    {
        WH::PrintLine(U"参数\"{}\"不是立方体纹理参数",ParameterName);
        return;
    }

    TextureCubes[ParameterDescription.Offset]=Texture;
    SyncParameterToRenderThread();
}

void RMaterial::SetTextureCubeByIndex(int64_t Index, TSharedObjectPtr<RTextureCube> Texture)
{
    if(Index>=TextureCubes.Num())
    {
        WH::PrintLine(U"材质中没有索引为{}的立方体纹理参数",Index);
        return;
    }

    TextureCubes[Index]=Texture;
    SyncParameterToRenderThread();
}

TSharedObjectPtr<RMaterial> RMaterial::Create(const SMaterialCreateInfo& InCreateInfo)
{
    auto Ret= NewObject<RMaterial>();
    Ret->VertexShader=InCreateInfo.VertexShader;
    Ret->FragmentShader=InCreateInfo.FragmentShader;
    Ret->Type=InCreateInfo.PipelineType;
    Ret->FloatParameters=InCreateInfo.FloatParameters;
    Ret->Texture2Ds=InCreateInfo.Texture2Ds;
    Ret->TextureCubes=InCreateInfo.TextureCubes;
    Ret->ParameterDescriptions=InCreateInfo.ParameterDescriptions;
    Ret->bDoubleSide=InCreateInfo.bDoubleSide;

    Ret->SyncToRenderThread();
    return Ret;
}

